home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 August: Tool Chest / Dev.CD Aug 00 TC Disk 2.toast / pc / sample code / interapplication comm / 7edit / source / svaetextutils.c < prev    next >
Encoding:
C/C++ Source or Header  |  2000-06-23  |  19.2 KB  |  814 lines

  1. /*
  2.     File:        SVAETextUtils.c
  3.  
  4.     Contains:    
  5.  
  6.     Written by: Original version by Jon Lansdell and Nigel Humphreys.
  7.                 3.1 updates by Greg Sutton.
  8.  
  9.     Copyright:    Copyright © 1995-1999 by Apple Computer, Inc., All Rights Reserved.
  10.  
  11.                 You may incorporate this Apple sample source code into your program(s) without
  12.                 restriction. This Apple sample source code has been provided "AS IS" and the
  13.                 responsibility for its operation is yours. You are not permitted to redistribute
  14.                 this Apple sample source code as "Apple sample source code" after having made
  15.                 changes. If you're going to re-distribute the source, we require that you make
  16.                 it clear in the source that the code was descended from Apple sample source
  17.                 code, but that you've made changes.
  18.  
  19.     Change History (most recent first):
  20.                             7/20/1999    Karl Groethe    Updated for Metrowerks Codewarror Pro 2.1
  21.                 
  22.  
  23. */
  24. #include "SVAETextUtils.h"
  25.  
  26. #include "SVEditAEUtils.h"
  27. #include "SVEditWindow.h"        // for DPtrFromWindowPtr()
  28. #include "SVAERecording.h"
  29.  
  30.  
  31. #include <AEPackObject.h>
  32.  
  33.  
  34.  
  35. // ----------------------------------------------------------------------------------
  36. //    Name:    PutStyledTextFromDescIntoTEHandle
  37. //    Purpose: Takes the text in an AEDesc containing typeIntlText and puts it in
  38. //             a styled text edit record at the current insertion point.
  39. //                     Looks for typeIntlText, typeStyledText, typeChar in that order.
  40. // ----------------------------------------------------------------------------------
  41.  
  42. OSErr    PutStyledTextFromDescIntoTEHandle(const AEDesc *sourceTextDesc, TEHandle theHTE)
  43. {
  44.     AEDesc styledTextDesc;
  45.     AEDesc textStyleDesc;
  46.     AEDesc rawTextDesc;
  47.     OSErr  myErr;
  48.     OSErr  ignoreErr;
  49.     
  50.     styledTextDesc.dataHandle = nil;
  51.     textStyleDesc.dataHandle  = nil;
  52.     rawTextDesc.dataHandle    = nil;
  53.     
  54.     //    Coerce to an AERecord and then extract the parts of the
  55.     //    styled text - works for typeIntlText, typeStyledText
  56.  
  57.     myErr = AECoerceDesc(sourceTextDesc, typeAERecord, &styledTextDesc);
  58.     
  59.     if (noErr == myErr)
  60.     {        
  61.         myErr = AEGetKeyDesc(&styledTextDesc,
  62.                                                  keyAEText,
  63.                                                  typeChar,
  64.                                                  &rawTextDesc);
  65.                                                  
  66.         myErr = AEGetKeyDesc(&styledTextDesc,
  67.                                                  keyAEStyles,
  68.                                                  typeScrapStyles,
  69.                                                  &textStyleDesc);
  70.     }
  71.     else
  72.     {
  73.         myErr = AECoerceDesc(sourceTextDesc, typeChar, &rawTextDesc);
  74.         
  75.         textStyleDesc.dataHandle = NULL; // so that TEStylInsert acts like TEInsert                
  76.     }
  77.             
  78.     HLock((Handle)rawTextDesc.dataHandle);
  79.     
  80.     TEDelete(theHTE);                    // Insert over current selection
  81.     TEStyleInsert((const void *) (*rawTextDesc.dataHandle),
  82.                                          GetHandleSize(rawTextDesc.dataHandle),
  83.                                              (StScrpHandle) textStyleDesc.dataHandle,
  84.                                                  theHTE);
  85.                              
  86.     HUnlock((Handle)rawTextDesc.dataHandle);
  87.     
  88.     if (textStyleDesc.dataHandle)
  89.         ignoreErr = AEDisposeDesc(&textStyleDesc);
  90.     
  91.     if (rawTextDesc.dataHandle)
  92.         ignoreErr = AEDisposeDesc(&rawTextDesc);
  93.     
  94.     if (styledTextDesc.dataHandle)
  95.         ignoreErr = AEDisposeDesc(&styledTextDesc);
  96.         
  97.     return(myErr);
  98. }
  99.  
  100.  
  101. TEHandle    TEHandleFromWindow(WindowPtr theWindow)
  102. {
  103.     DPtr        docPtr;
  104.     TEHandle    result = NULL;
  105.     
  106.     if (! theWindow)
  107.         return(NULL);
  108.     
  109.     docPtr = DPtrFromWindowPtr(theWindow);
  110.     
  111.     if (docPtr)
  112.         result = docPtr->theText;
  113.         
  114.     return(result);
  115. }
  116.  
  117. TEHandle    TEHandleFromTextToken(TextToken* aToken)
  118. {
  119.     if (! aToken)
  120.         return(NULL);
  121.         
  122.     return(TEHandleFromWindow(aToken->tokenWindow));
  123. }
  124.  
  125.  
  126. OSErr    GetInsertDescFromInsertHere(AEDesc* insertHereDesc, AEDesc* insertDesc, DescType* insertType)
  127. {
  128.     AEDesc        insertRec = {typeNull, NULL},
  129.                 objectSpec = {typeNull, NULL};
  130.     DescType    returnedType;
  131.     Size        actualSize;
  132.     OSErr        err;
  133.  
  134.      switch (insertHereDesc->descriptorType)
  135.      {
  136.          case typeInsertionLoc:
  137.              err = AECoerceDesc(insertHereDesc, typeAERecord, &insertRec);
  138.              if (noErr != err) goto done;
  139.              
  140.              err = AEGetKeyPtr(&insertRec, keyAEPosition, typeEnumeration, &returnedType,
  141.                                          (Ptr)insertType, sizeof(insertType), &actualSize);
  142.              if (noErr != err) goto done;
  143.  
  144.             err = AEGetKeyDesc(&insertRec, keyAEObject, typeWildCard, &objectSpec);
  145.             if (objectSpec.descriptorType != typeNull)
  146.             {
  147.                 err = AEResolve(&objectSpec, kAEIDoMinimum, insertDesc);
  148.                 if (err != noErr) goto done;
  149.             }
  150.              break;
  151.      
  152.          case typeObjectSpecifier:
  153.              err = AEResolve(insertHereDesc, kAEIDoMinimum, insertDesc);
  154.              if (noErr != err) goto done;
  155.              *insertType = insertDesc->descriptorType;
  156.              break;
  157.              
  158.          case typeNull:                    // No insertion location given
  159.              *insertType = typeNull;
  160.              break;
  161.              
  162.          default:                        // Just copy the descriptor
  163.              err = AEDuplicateDesc(insertHereDesc, insertDesc);
  164.              if (noErr != err) goto done;
  165.              *insertType = insertDesc->descriptorType;
  166.      }
  167.      
  168.  done:
  169.      if (insertRec.dataHandle)
  170.          AEDisposeDesc(&insertRec);
  171.      if (objectSpec.dataHandle)
  172.          AEDisposeDesc(&objectSpec);
  173.      
  174.      return(err);
  175. }
  176.  
  177. // This routine returns an enumerated type describing the relative position
  178. // of one TextToken to another.
  179.  
  180. TokenWithinType    TokenWithinToken(TextToken* container, TextToken* token, short* numPartial)
  181. {
  182.     TokenWithinType        result;
  183.  
  184.     if (token->tokenOffset + token->tokenLength < container->tokenOffset)
  185.         result = kTokenBefore;
  186.     else if (container->tokenOffset + container->tokenLength < token->tokenOffset)
  187.         result = kTokenAfter;
  188.     else if (token->tokenOffset >= container->tokenOffset
  189.                 && token->tokenOffset + token->tokenLength <= container->tokenOffset + container->tokenLength)
  190.         result = kTokenWithin;
  191.     else if (token->tokenOffset < container->tokenOffset)
  192.     {
  193.         result = kTokenPartialBefore;
  194.         if (numPartial)
  195.             *numPartial = token->tokenOffset + token->tokenLength - container->tokenOffset;
  196.     }
  197.     else
  198.     {
  199.         result = kTokenPartialAfter;
  200.         if (numPartial)
  201.             *numPartial = container->tokenOffset + container->tokenLength - token->tokenOffset;
  202.     }
  203.     
  204.     return(result);
  205. }
  206.  
  207.  
  208. OSErr    TextTokenFromWindowToken(WindowToken* theWindowToken, TextToken* theTextToken)
  209. {
  210.     DPtr        docPtr;
  211.  
  212.     docPtr = DPtrFromWindowPtr(theWindowToken->tokenWindow);
  213.  
  214.     if (! docPtr)
  215.         return(errAENoSuchObject);
  216.             // Create our text token
  217.     theTextToken->tokenWindow = theWindowToken->tokenWindow;    
  218.     theTextToken->tokenOffset = 1;                                // Start at 1
  219.     theTextToken->tokenLength = (**docPtr->theText).teLength;    // through whole length
  220.     
  221.     return(noErr);
  222. }
  223.  
  224.  
  225. OSErr    TextTokenFromWindowDesc(AEDesc* windowDesc, TextToken* theToken)
  226. {
  227.     AEDesc            aDesc = {typeNull, NULL};
  228.     WindowToken        aWindowToken;
  229.     Size            actualSize;
  230.     OSErr            err;
  231.     
  232.     err = AECoerceDesc(windowDesc, typeMyWndw, &aDesc);
  233.     if (noErr != err) goto done;
  234.         
  235.     GetRawDataFromDescriptor(&aDesc, (Ptr)&aWindowToken,
  236.                                     sizeof(aWindowToken), &actualSize);
  237.  
  238.     err = TextTokenFromWindowToken(&aWindowToken, theToken);
  239.     
  240. done:    
  241.     if (aDesc.dataHandle)
  242.         AEDisposeDesc(&aDesc);
  243.  
  244.     return(err);
  245. }
  246.  
  247.  
  248. OSErr    TextDescFromWindowToken(WindowToken* theWindowToken, AEDesc* textDesc)
  249. {
  250.     TextToken    aToken;
  251.     OSErr        err;
  252.     
  253.     err = TextTokenFromWindowToken(theWindowToken, &aToken);
  254.     if (noErr != err) goto done;
  255.     
  256.     err = AECreateDesc(typeMyText, (Ptr)&aToken, sizeof(aToken), textDesc);
  257.  
  258. done:
  259.     return(err);
  260. }
  261.  
  262.  
  263. OSErr    TextDescFromWindowDesc(AEDesc* windowDesc, AEDesc* textDesc)
  264. {
  265.     TextToken    aToken;
  266.     OSErr        err;
  267.     
  268.     err = TextTokenFromWindowDesc(windowDesc, &aToken);
  269.     if (noErr != err) goto done;
  270.     
  271.     err = AECreateDesc(typeMyText, (Ptr)&aToken, sizeof(aToken), textDesc);
  272.  
  273. done:
  274.     return(err);
  275. }
  276.  
  277.  
  278. void MoveToNonSpace(short *start, short limit, charsHandle myChars)
  279.     // Treats space, comma, full stop, ; and : as space chars
  280.     short x;
  281.  
  282.     while (*start <= limit) {
  283.       x = (**myChars)[*start];
  284.         if (IsWhiteSpace(x))
  285.             (*start) +=1;
  286.         else
  287.             return;
  288.     }
  289. }
  290.     
  291. void    MoveToSpace(short *start, short limit, charsHandle myChars)
  292.     // Treats space,comma, full stop, ; and : as space chars
  293.     short x;
  294.     
  295.     while (*start <= limit)
  296.     {
  297.         x = (**myChars)[*start];
  298.         if (! IsWhiteSpace(x))
  299.             (*start)++;
  300.         else
  301.             return;
  302.     }
  303. }
  304.  
  305. void    MoveToEndOfParagraph(short *start, short limit, charsHandle myChars)
  306.     //    Treats CR as end of paragraph
  307.     short x;
  308.     
  309.     while (*start <= limit)
  310.     {
  311.         x = (**myChars)[*start];
  312.         if (! IsParagraphDelimiter(x))        // had x != CR
  313.             (*start)++;
  314.         else
  315.             return;
  316.     }
  317. }
  318.  
  319.  
  320. // This routine counts the given elementType between startAt and 
  321.  
  322. OSErr    CountTextElements(TEHandle inTextHandle, short startAt,
  323.                                 short forHowManyChars, DescType elementType, short* result)
  324. {
  325.     charsHandle    theChars;
  326.     short       limit,
  327.                 start;
  328.     OSErr        err = noErr;
  329.  
  330.     switch (elementType)
  331.     {
  332.         case cInsertionPoint:    // Always one more insertion location than characters
  333.             *result = forHowManyChars + 1;
  334.             break;
  335.             
  336.         case cChar:            // Easy
  337.             *result = forHowManyChars;
  338.             break;
  339.             
  340.         case cText:    
  341.             *result = 1;
  342.             break;
  343.         
  344.         case cWord:            // Cycle through - counting
  345.         case cParagraph:
  346.             theChars = (charsHandle)(**inTextHandle).hText;
  347.             start = startAt - 1;                    // Convert to zero based
  348.             limit = start + forHowManyChars - 1;    // when passed one based
  349.             *result    = 0;
  350.             MoveToNonSpace(&start, limit, theChars);
  351.             while (start <= limit)
  352.             {
  353.                 (*result)++;
  354.                 switch (elementType)
  355.                 {
  356.                     case cWord:
  357.                         MoveToSpace(&start, limit, theChars);
  358.                         break;
  359.                         
  360.                     case cParagraph:
  361.                         MoveToEndOfParagraph(&start, limit, theChars);
  362.                         break;
  363.                 }
  364.                 MoveToNonSpace(&start, limit, theChars);
  365.             }
  366.             break;
  367.     
  368.         default:
  369.             *result = -1;
  370.             err = errAEBadKeyForm;
  371.     }
  372.     
  373.     return(err);
  374. } // CountTextElements
  375.  
  376. OSErr    GetDescOfNthTextElement(short index, DescType elementType,
  377.                                         TextToken* containerToken, AEDesc* result)
  378. {
  379.     DPtr        docPtr;
  380.     TextToken    theToken;
  381.     short        start,
  382.                 maxChars,
  383.                 elementCount,
  384.                 limit,
  385.                 elementStart;
  386.     charsHandle    theChars;
  387.     OSErr        err;
  388.     
  389.     if (! containerToken)
  390.         return(errAEEmptyListContainer);
  391.  
  392.     docPtr = DPtrFromWindowPtr(containerToken->tokenWindow);
  393.     start = containerToken->tokenOffset - 1;    // Zero based
  394.     maxChars = containerToken->tokenLength;
  395.  
  396.     err = CountTextElements(docPtr->theText, containerToken->tokenOffset,
  397.                                         maxChars, elementType, &elementCount);
  398.     if (noErr != err) return(err);
  399.     
  400.     if (index < 0)                        // Change a negative index to positive
  401.         index = elementCount + index + 1;
  402.         
  403.     if (index > elementCount)            // Got given an index out of range
  404.         return(errAEIllegalIndex);
  405.         
  406.             // Set the window that the token relates to
  407.     theToken.tokenWindow = containerToken->tokenWindow;
  408.  
  409.     switch (elementType)
  410.     {
  411.         case cInsertionPoint:
  412.             theToken.tokenOffset = start + index - 1;
  413.             theToken.tokenLength = 0;
  414.             break;
  415.             
  416.         case cChar:        // Easy - just the start point + the index
  417.             theToken.tokenOffset = start + index;
  418.             theToken.tokenLength = 1;
  419.             break;
  420.             
  421.         case cText:    
  422.             theToken.tokenOffset = start + index;
  423.             theToken.tokenLength = maxChars;
  424.             break;
  425.             
  426.         case cWord:
  427.         case cParagraph:
  428.             theChars = (charsHandle)(**(docPtr->theText)).hText;
  429.             limit = start + maxChars - 1;
  430.             MoveToNonSpace(&start, limit, theChars);
  431.             while ((start <= limit) && (index > 0))
  432.             {
  433.                 index--;
  434.                 elementStart = start;
  435.                 switch (elementType)
  436.                 {
  437.                     case cWord:
  438.                         MoveToSpace(&start, limit, theChars);
  439.                         break;
  440.                         
  441.                     case cParagraph:
  442.                         MoveToEndOfParagraph(&start, limit, theChars);
  443.                         break;
  444.                 }
  445.                 theToken.tokenLength = start - elementStart;
  446.                 MoveToNonSpace(&start, limit, theChars);
  447.             }
  448.             theToken.tokenOffset = elementStart + 1;    // Convert to one based
  449.             break;
  450.     }
  451.  
  452.     err = AECreateDesc(typeMyText, (Ptr)&theToken, sizeof(theToken), result);
  453.  
  454.     return(err);
  455. }
  456.  
  457.  
  458. char    GetTEHChar(TEHandle aTEH, short offset)
  459. {
  460.     char    result;
  461.     
  462.     offset--;        // This is now 0 based
  463.  
  464.     if (offset < 0 || offset >= (*aTEH)->teLength)
  465.         return('\0');
  466.         
  467.     result = *(char *)((*(**aTEH).hText) + offset);
  468.     
  469.     return(result);
  470. }
  471.  
  472. Boolean        IsAtStart(TextToken* theToken)
  473. {
  474.     Boolean    result;
  475.                     // Is at start if offset is at 1
  476.     result = (theToken->tokenOffset == 1);
  477.     
  478.     return(result);
  479. }
  480.  
  481. Boolean        IsAtEnd(TextToken* theToken)
  482. {
  483.     TEHandle    aTEH;
  484.     Boolean        result;
  485.     
  486.     aTEH = TEHandleFromTextToken(theToken);
  487.                     // Does it go to the end?
  488.     result = (theToken->tokenOffset + theToken->tokenLength >= (**aTEH).teLength);
  489.     
  490.     return(result);
  491. }
  492.  
  493. Boolean        IsWhiteSpace(short aChar)
  494. {
  495.     Boolean    result;
  496.  
  497.     result = (aChar == ' ' || aChar == ',' || aChar == '.'
  498.                 || aChar == ':' || aChar == LF || aChar == CR);
  499.              
  500.     return(result);
  501. }
  502.  
  503. Boolean        IsParagraphDelimiter(short aChar)
  504. {
  505.     Boolean    result;
  506.  
  507.     result = (aChar == CR);
  508.              
  509.     return(result);
  510. }
  511.  
  512. Boolean        IsContentsToken(TextToken* theToken)
  513. {
  514.     return(IsAtStart(theToken) && IsAtEnd(theToken));
  515. }
  516.  
  517. Boolean        IsParagraphToken(TextToken* theToken, short* start, short* end)
  518. {
  519.     TEHandle    aTEH;
  520.     OSErr        err;
  521.     short        number;
  522.     Boolean        fStart,
  523.                 fEnd,
  524.                 result;
  525.     
  526. //    if (IsContentsToken(theToken))    // So we don't start and end 
  527. //        return(false);
  528.         
  529.     aTEH = TEHandleFromTextToken(theToken);
  530.         
  531.                                     // What about having CR's before end??
  532.                                     // - then it's just more paragraphs?
  533.                                     // - in STE yes - ahhh it'll be okay
  534.         
  535.     fStart = IsAtStart(theToken) || IsParagraphDelimiter(GetTEHChar(aTEH, theToken->tokenOffset - 1));
  536.     fEnd = IsAtEnd(theToken) || IsParagraphDelimiter(GetTEHChar(aTEH, theToken->tokenOffset + theToken->tokenLength));
  537.     
  538.     if (fStart && fEnd)
  539.     {
  540.         // need to do a count of the paragraphs
  541.         
  542.         err = CountTextElements(aTEH, theToken->tokenOffset,
  543.                             theToken->tokenLength, cParagraph, &number);
  544.  
  545.         // count text elements before it i.e. offset == 0 limit == theToken->tokenOffset
  546.         
  547.         if (IsAtStart(theToken))
  548.             *start = 1;
  549.         else
  550.         {                // From beginning to charracter before start of paragraph
  551.             err = CountTextElements(aTEH, 1,theToken->tokenOffset - 1, cParagraph, start);
  552.             (*start)++;
  553.         }
  554.         
  555.         *end = *start + number - 1;
  556.         
  557.         result = true;
  558.     }
  559.     else
  560.         result = false;
  561.     
  562.     return(result);
  563. }
  564.  
  565. Boolean        IsWordToken(TextToken* theToken, short* start, short* end)
  566. {
  567.     TEHandle    aTEH;
  568.     OSErr        err;
  569.     short        number;
  570.     Boolean        fStart,
  571.                 fEnd,
  572.                 result;
  573.     
  574. //    if (IsContentsToken(theToken) || IsParagraphToken(theToken, start, end))
  575. //        return(false);
  576.     
  577.     aTEH = TEHandleFromTextToken(theToken);
  578.         
  579.                                     // What about having CR's before end??
  580.                                     // - then it's just more paragraphs?
  581.                                     // - in STE yes - ahhh it'll be okay
  582.         
  583.     fStart = IsAtStart(theToken) || IsWhiteSpace(GetTEHChar(aTEH, theToken->tokenOffset - 1));
  584.     fEnd = IsAtEnd(theToken) || IsWhiteSpace(GetTEHChar(aTEH, theToken->tokenOffset + theToken->tokenLength));
  585.     
  586.     if (fStart && fEnd)
  587.     {
  588.         // need to do a count of the words
  589.         
  590.         err = CountTextElements(aTEH, theToken->tokenOffset,
  591.                             theToken->tokenLength, cWord, &number);
  592.  
  593.         // count text elements before it i.e. offset == 0 limit == theToken->tokenOffset
  594.         
  595.         if (IsAtStart(theToken))
  596.             *start = 1;
  597.         else
  598.         {                // From beginning to charracter before start of word
  599.             err = CountTextElements(aTEH, 1, theToken->tokenOffset - 1, cWord, start);
  600.             (*start)++;
  601.         }
  602.         
  603.         *end = *start + number - 1;
  604.         
  605.         result = true;
  606.     }
  607.     else
  608.         result = false;
  609.     
  610.     return(result);
  611. }
  612.  
  613.  
  614. DescType    GetTextTokenType(TextToken* theToken, short* start, short* end)
  615. {
  616.     DescType    result;
  617.     
  618.     *start = *end = -1;                    // Just set to the same value
  619.  
  620.     if (! theToken->tokenLength)
  621.     {
  622.         result = cInsertionPoint;
  623.     }
  624.     else if (IsContentsToken(theToken))
  625.     {
  626.         result = pContents;
  627.     }
  628.     else if (IsParagraphToken(theToken, start, end))
  629.     {
  630.         result = cParagraph;
  631.     }
  632.     else if (IsWordToken(theToken, start, end))
  633.     {
  634.         result = cWord;
  635.     }
  636.     else
  637.     {
  638.         result = cChar;
  639.         *start = theToken->tokenOffset;
  640.         *end = theToken->tokenOffset + theToken->tokenLength - 1;
  641.     }
  642.     
  643.     return(result);
  644. }
  645.  
  646. OSErr    MakeContentsSpecifier(TextToken* theToken, AEDesc* result)
  647. {
  648.     AEDesc        docSpec = {typeNull, NULL},
  649.                 contentsDesc = {typeNull, NULL};
  650.     DescType    propertyID;
  651.     OSErr        err;
  652.  
  653.     err = MakeDocumentObj(theToken->tokenWindow, &docSpec);
  654.     if (noErr != err) goto done;
  655.     
  656.     propertyID = pContents;
  657.     err = AECreateDesc(typeType, (Ptr)&propertyID, sizeof(DescType), &contentsDesc);
  658.     if (err != noErr) goto done;
  659.     err = CreateObjSpecifier(cProperty, &docSpec, formPropertyID, &contentsDesc, false, result);
  660.  
  661. done:
  662.     if (docSpec.dataHandle)
  663.         AEDisposeDesc(&docSpec);
  664.     if (contentsDesc.dataHandle)
  665.         AEDisposeDesc(&contentsDesc);
  666.     
  667.     return(err);
  668. }
  669.  
  670.  
  671. OSErr    MakeAbsoluteTextSpecifier(WindowPtr theWindow, DescType textType, long index, AEDesc* result)
  672. {
  673.     AEDesc        docSpec = {typeNull, NULL},
  674.                 absoluteDesc = {typeNull, NULL};
  675.     OSErr        err;
  676.     
  677.     if (theWindow)
  678.     {
  679.         err = MakeDocumentObj(theWindow, &docSpec);
  680.         if (noErr != err) goto done;
  681.     }
  682.     // else just use the NULL'ed value
  683.  
  684.     err = AECreateDesc(typeLongInteger, (Ptr)&index, sizeof(index), &absoluteDesc);
  685.     if (err != noErr) goto done;
  686.     err = CreateObjSpecifier(textType, &docSpec, formAbsolutePosition,
  687.                                                     &absoluteDesc, false, result);
  688.  
  689. done:
  690.     if (docSpec.dataHandle)
  691.         AEDisposeDesc(&docSpec);
  692.     if (absoluteDesc.dataHandle)
  693.         AEDisposeDesc(&absoluteDesc);
  694.     
  695.     return(err);
  696. }
  697.  
  698.  
  699. OSErr    MakeInsertionPointSpecifier(TextToken* theToken, AEDesc* result)
  700. {
  701.     AEDesc        relativeToSpec,
  702.                 relativeDesc;
  703.     DescType    relativeType;
  704.     OSErr        err;
  705.  
  706.     if (IsAtStart(theToken))            // Before contents (whether there are any or not)
  707.     {
  708.         relativeType = kAEPrevious;
  709.         err = MakeContentsSpecifier(theToken, &relativeToSpec);
  710.     }
  711.     else if (IsAtEnd(theToken))            // After last character
  712.     {
  713.         relativeType = kAENext;
  714.         //err = MakeContentsSpecifier(theToken, &relativeToSpec);
  715.         err = MakeAbsoluteTextSpecifier(theToken->tokenWindow, cChar, -1, &relativeToSpec);
  716.     }
  717.     else                                // Has a character it can go before
  718.     {
  719.         relativeType = kAEPrevious;
  720.         err = MakeAbsoluteTextSpecifier(theToken->tokenWindow, cChar, theToken->tokenOffset, &relativeToSpec);
  721.     }
  722.  
  723.     if (noErr != err) goto done;
  724.     
  725.       err = AECreateDesc(typeEnumerated, &relativeType, sizeof(relativeType), &relativeDesc);
  726.     if (noErr != err) goto done;
  727.     err = CreateObjSpecifier(cInsertionPoint, &relativeToSpec, formRelativePosition,
  728.                                                                    &relativeDesc, false, result);
  729.  
  730. done:
  731.     if (relativeToSpec.dataHandle)
  732.         AEDisposeDesc(&relativeToSpec);
  733.     if (relativeDesc.dataHandle)
  734.         AEDisposeDesc(&relativeDesc);
  735.  
  736.     return(err);
  737. }
  738.  
  739. OSErr    GetIndexSpecifier(TextToken* theToken, DescType textType, long index, AEDesc* result)
  740. {
  741.     OSErr    err;
  742.  
  743.     switch (textType)
  744.     {
  745.         case cInsertionPoint:
  746.             err = MakeInsertionPointSpecifier(theToken, result);
  747.             break;
  748.             
  749.         case pContents:
  750.             err = MakeContentsSpecifier(theToken, result);
  751.             break;
  752.             
  753.         case cParagraph:
  754.         case cWord:
  755.         case cChar:
  756.             err = MakeAbsoluteTextSpecifier(theToken->tokenWindow, textType, index, result);
  757.             break;
  758.         
  759.         default:
  760.             err = errAETypeError;
  761.     }
  762.     
  763.     return(err);
  764. }
  765.  
  766.  
  767. OSErr    GetTextTokenObjectSpecifier(TextToken* theToken, AEDesc* result)
  768. {
  769.     AEDesc        docSpec = {typeNull, NULL},
  770.                 startSpec = {typeNull, NULL},
  771.                 endSpec = {typeNull, NULL},
  772.                 rangeDesc = {typeNull, NULL};
  773.     DescType    textType;
  774.     short        start,
  775.                 end;
  776.     OSErr        err;
  777.     
  778.     textType = GetTextTokenType(theToken, &start, &end);
  779.     
  780.     err = GetIndexSpecifier(theToken, textType, start, &startSpec);
  781.     if (noErr != err) goto done;
  782.  
  783.     if (start != end)        // Sort out rest of range specifier
  784.     {
  785.         err = GetIndexSpecifier(theToken, textType, end, &endSpec);
  786.         if (noErr != err) goto done;
  787.         
  788.         err = CreateRangeDescriptor(&startSpec, &endSpec, false, &rangeDesc);
  789.         if (noErr != err) goto done;
  790.  
  791.         err = MakeDocumentObj(theToken->tokenWindow, &docSpec);
  792.         if (noErr != err) goto done;
  793.  
  794.         err = CreateObjSpecifier(cText, &docSpec, formRange, &rangeDesc, false, result);
  795.     }
  796.     else
  797.         err = AEDuplicateDesc(&startSpec, result);
  798.     
  799. done:
  800.     if (docSpec.dataHandle)
  801.         AEDisposeDesc(&docSpec);
  802.     if (startSpec.dataHandle)
  803.         AEDisposeDesc(&startSpec);
  804.     if (endSpec.dataHandle)
  805.         AEDisposeDesc(&endSpec);
  806.     if (rangeDesc.dataHandle)
  807.         AEDisposeDesc(&rangeDesc);
  808.  
  809.     return(err);
  810. }
  811.